package com.goparty.data.dao;

import java.util.Arrays;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.goparty.data.constant.EventVisibility;
import com.goparty.data.constant.InvitationStatus;
import com.goparty.data.constant.ParticipanceType;
import com.goparty.data.exception.BaseException;
import com.goparty.data.model.Event;
import com.goparty.data.model.EventApplication;
import com.goparty.data.model.EventInvitation;
import com.goparty.data.model.EventMember;
import com.goparty.data.model.EventMemberId;
import com.goparty.data.model.User;
import com.goparty.data.repository.IEventApplicationRepository;
import com.goparty.data.repository.IEventInvitationRepository;
import com.goparty.data.repository.IEventMemberRepository;
import com.goparty.data.repository.IEventRepository;


@Repository("eventDao")
@Transactional
public class EventDao {
	private static Log log = LogFactory.getLog(EventDao.class);
	
	@PersistenceContext
	private EntityManager em;
	
	@Autowired
	private IEventRepository eventRepository;
	
	@Autowired
	private IEventInvitationRepository eventInvitationRepository;
	
	@Autowired
	private IEventApplicationRepository eventApplicationRepository;
	
	@Autowired
	private IEventMemberRepository eventMemberRepository;
	

	public void saveEventMember(EventMember eventMember){
		eventMemberRepository.save(eventMember);
	} 
	
	public EventMember readEventMember(EventMemberId id){
		EventMember eventMember = eventMemberRepository.findOne(id);
		return eventMember;
	} 
	
	public List<EventMember> findByEventIdAndAdmin(String eventId,String admin){
		StringBuffer sb = new StringBuffer();
		sb.append("select ge.* from gp_event_member ge  ");
		sb.append("where eventId =:eventId and admin=:admin ");
		Query  query = em.createNativeQuery(sb.toString(), EventMember.class);
		query.setParameter("eventId", eventId);
		query.setParameter("admin", admin);
		return query.getResultList();
	}
	 
	public Event read(String id) {
		Event event = eventRepository.findOne(id);
		if(event == null){
			throw new BaseException("Event not exist!");
		}
		User owner = event.getOwner();
		owner.setBirthdate(null); 
		owner.setParticipance(ParticipanceType.N);
		for(User user : event.getMembers()){
			EventMemberId emId = new EventMemberId();
			emId.setEventId(event.getId());
			emId.setUserId(user.getId());
			EventMember eventMember = eventMemberRepository.findOne(emId);
			user.setAdmin(eventMember.isAdmin());
			user.setParticipance(eventMember.getParticipance());
			if(user.getId().equals(owner.getId())){
				owner.setAdmin(eventMember.isAdmin());
				owner.setParticipance(eventMember.getParticipance());
			}
		}
		
		return event;
	}

	public Event create(Event event) {
		 em.persist(event);
		 em.flush();
		 for(User u : event.getMembers()){
			 if(u.isAdmin()){
				 Query query = em.createNativeQuery("update gp_event_member set admin='Y' where eventId=? and userId=?");
				query.setParameter(1, event.getId());
				query.setParameter(2, u.getId());
				query.executeUpdate(); 
			 }
		 }		 
		 em.refresh(event);
		 return event;
	}

	public Event update(Event event) {
		em.merge(event);
		return event;
		
	}

	public boolean delete(String id) {
		boolean ret = false;		
		try{
			eventRepository.delete(id);
			ret = true;
		}catch(Exception ex){
			log.error("del user error",ex);
			throw ex;
		}		
		return ret;
	}

	public List<Event> getEvents(String userId,String scope, Date after, Date before,
			String categories, String search, long offset, long limit){
		StringBuffer sb = new StringBuffer();
		sb.append("select ge.* from gp_event ge  ");
		sb.append("where 1=1 ");
		sb.append("and ge.startTime<=:before and ge.endTime>=:after ");
		if (search!=null&&!"".equals(search)){			
			sb.append("and (ge.title in (:keys) or ge.description in (:keys)) ");
		}
		if (categories!=null&&!"".equals(categories)){			
			sb.append(" and  exists (select 1 from gp_event_category gec where gec.eventId = ge.id and gec.cateId in (:categories))  ");
		}
		if ("friends".equals(scope)){
			sb.append(" and  exists (select 1 from gp_user_friend guf where ge.ownerId = guf.friendId and guf.userId =:userId and ge.visibility = :visibility)  ");
		} else if ("all".equals(scope)){
			sb.append(" and ge.visibility = :visibility  ");
		} else {
			sb.append(" and exists (select 1 from gp_event_member gea where ge.id = gea.eventId and gea.userId =:userId)  ");
		} 
		sb.append("limit " + offset + "," + limit);
		Query  query = em.createNativeQuery(sb.toString(), Event.class);

		if (search!=null&&!"".equals(search)){
			String[] keys = search.split("\\|");
			List<String> keyList = Arrays.asList(keys);
			query.setParameter("keys", keyList);
		}
		if (categories!=null&&!"".equals(categories)){
			String[] categoryArray = categories.split("\\|");
			List<String> categoryList = Arrays.asList(categoryArray);
			query.setParameter("categories", categoryList);			
		}	
		if ("friends".equals(scope)){
			query.setParameter("userId", userId);
			query.setParameter("visibility", EventVisibility.V_FRIEND);
		} else if ("all".equals(scope)){
			query.setParameter("visibility", EventVisibility.V_PUBLIC.ordinal());
		} else {
			query.setParameter("userId", userId);
		} 
		query.setParameter("after", after);
		query.setParameter("before", before);
		return query.getResultList();
	}	
	
	public List<Event> getUserEvents(String myuserId,String userId, Date after, Date before,
			String categories, String search, long offset, long limit){
		StringBuffer sb = new StringBuffer();
		sb.append("select ge.* from gp_event ge  ");
		sb.append("where 1=1 ");
		sb.append("and ge.startTime<=:before and ge.endTime>=:after ");
		sb.append("and ge.ownerId =:ownerId ");
		if (search!=null&&!"".equals(search)){			
			sb.append("and (ge.title in (:keys) or ge.description in (:keys)) ");
		}
		if (categories!=null&&!"".equals(categories)){			
			sb.append(" and  exists (select 1 from gp_event_category gec where gec.eventId = ge.id and gec.cateId in (:categories))  ");
		}
		sb.append(" and  ( exists (select 1 from gp_user_friend guf where ge.ownerId = guf.friendId and guf.userId =:userId and ge.visibility = :visibility)  ");
		sb.append("       or ge.visibility = :pubvisibility)   ");
		
		sb.append("limit " + offset + "," + limit);
		Query  query = em.createNativeQuery(sb.toString(), Event.class);

		if (search!=null&&!"".equals(search)){
			String[] keys = search.split("\\|");
			List<String> keyList = Arrays.asList(keys);
			query.setParameter("keys", keyList);
		}
		if (categories!=null&&!"".equals(categories)){
			String[] categoryArray = categories.split("\\|");
			List<String> categoryList = Arrays.asList(categoryArray);
			query.setParameter("categories", categoryList);			
		}	
		query.setParameter("ownerId", userId);
		query.setParameter("userId", myuserId);
		query.setParameter("visibility", EventVisibility.V_FRIEND);
		query.setParameter("pubvisibility", EventVisibility.V_PUBLIC.ordinal());
		
		query.setParameter("after", after);
		query.setParameter("before", before);
		return query.getResultList();
	}	

	
	//invitation
	public void addInvitation(EventInvitation invitation){
		eventInvitationRepository.save(invitation);
	}
	public EventInvitation getInvitation(String invitationId){
		return eventInvitationRepository.findOne(invitationId);
	}
	public void updateInvitation(EventInvitation invitation){
		eventInvitationRepository.save(invitation);
	}
	
	//application
	public void addApplication(EventApplication application){
		eventApplicationRepository.save(application);
	}
	public EventApplication getApplication(String applicationId){
		return eventApplicationRepository.findOne(applicationId);
	}
	public void updateApplication(EventApplication application){
		eventApplicationRepository.save(application);
	}
		
	public List<EventInvitation> getUnrespondedInvitations(String inviteeId, long offset,
			long limit) {
		Pageable pageable = new PageRequest((int)offset,(int)limit); 
		return eventInvitationRepository.findByinviteeIdAndStatus(inviteeId, InvitationStatus.INIT, pageable);
	}

	public List<EventInvitation>  getRespondedInvitations(String inviteeId, long offset,
			long limit) {
		Pageable pageable = new PageRequest((int)offset,(int)limit); 
		return eventInvitationRepository.findByinviteeIdAndStatus(inviteeId, InvitationStatus.RESP, pageable);
	}
		
	public List<EventApplication>  getUnrespondedApplications(String approverId, long offset,
			long limit) {
		Pageable pageable = new PageRequest((int)offset,(int)limit); 
		return eventApplicationRepository.findByapproverIdAndStatus(approverId, InvitationStatus.INIT, pageable);
	}

	public List<EventApplication> getRespondedApplications(String approverId, long offset,
			long limit) {
		Pageable pageable = new PageRequest((int)offset,(int)limit); 
		return eventApplicationRepository.findByapproverIdAndStatus(approverId, InvitationStatus.RESP, pageable);
	}
	
	public List<EventApplication> findByEventIdAndStatus(String eventId,InvitationStatus status){
		return eventApplicationRepository.findByEventIdAndStatus(eventId, status);
	}
	
}
